/* 3DGo Game
 * Copyright (C) 2008 Roman Hwang, Eugene Melnichek

 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation - version 2
 * of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

/****************************************
Revision History

1.01 2008-05-17 Eugene Melnichek
Added keyboard input processing through the DirectInput8
Removed old input processing
Changed directional light vector
Moved sprite creation from d3dUtility.cpp

1.00 Roman Hwang
Initial revision
****************************************/

#include "d3dx9core.h"
#include "DInput.h"

#include "GameEngine.h"

#include "CompPlayer.h"
#include "FPSCounter.h"
#include "TextRenderer.h"
#include "Utility.h"

#include "../AudioPlayer/AudioPlayer.h"

// Globals
IDirect3DDevice9* pMyDevice;
IDirectInput8 * dInput8 = NULL;
IDirectInputDevice8 * diKeyboard = NULL, * diMouse = NULL;

// Statics
const float GameEngine::DIST_TO_BOARD = 10.0f;

/////////////////////////////////////////////////////////////////////////////////


GameEngine::GameEngine( int _boardSize ):m_board( _boardSize ), m_interfaceSprite(NULL), m_interfaceTexture(NULL),
cameraXAngle(-D3DX_PI * 0.5f), cameraYAngle(-D3DX_PI * 0.5f), processInput(true), _isQuiting(false),
soundVolume(NULL, 20, 200, 60, 40, 0, 100)
{
	m_playerWhite = new CompPlayer(WHITE);
	m_playerBlack = new CompPlayer(BLACK);
	_activePlayer = m_playerBlack;

	_audioPlayer = new Audio::AudioPlayer();
	soundVolume.SetAudioPlayer(_audioPlayer);
}


/////////////////////////////////////////////////////////////////////////////////


GameEngine::~GameEngine()
{
	delete m_playerWhite;
	delete m_playerBlack;
	delete FPSCounter::GetInstancePtr();
	if (dInput8)
	{
		if (diKeyboard)
		{
			diKeyboard->Unacquire();
			diKeyboard->Release();
		}
		if (diMouse)
		{
			diMouse->Unacquire();
			diMouse->Release();
		}
		dInput8->Release();
	}
	m_interfaceTexture->Release();
	m_interfaceSprite->Release();
	pMyDevice->Release();

	delete _audioPlayer;
}


/////////////////////////////////////////////////////////////////////////////////


void GameEngine::SetupView()
{
	pMyDevice->SetRenderState(D3DRS_LIGHTING, true);

	//D3DXVECTOR3 dir(1.0f, -1.0f, 1.0f);
	D3DXVECTOR3 dir(0.0f, 0.0f, 1.0f);
	D3DXCOLOR color(d3d::WHITE);
	D3DLIGHT9 light = d3d::CreateDirectionalLight(&dir, &color);

	pMyDevice->SetLight(0, &light);
	pMyDevice->LightEnable(0, true);

	pMyDevice->SetRenderState(D3DRS_NORMALIZENORMALS, true);
	pMyDevice->SetRenderState(D3DRS_SPECULARENABLE, true);

	D3DXMATRIX view;
	D3DXVECTOR3 pos(0.0f, 0.0f, -DIST_TO_BOARD);
	D3DXVECTOR3 target( 0.0f, 0.0f, 0.0f);
	D3DXVECTOR3 up( 0.0f, 1.0f, 0.0f);
	D3DXMatrixLookAtLH(&view, &pos, &target, &up);

	pMyDevice->SetTransform( D3DTS_VIEW, &view );

	D3DXMATRIX proj;
	D3DXMatrixPerspectiveFovLH(&proj, D3DX_PI * 0.25, (float)WIDTH / (float)HEIGHT, 1.0f, 1000.0f);

	pMyDevice->SetTransform( D3DTS_PROJECTION, &proj );

}


/////////////////////////////////////////////////////////////////////////////////


void GameEngine::Init(HINSTANCE _hInstance, WNDPROC WndProc)
{
	if(!d3d::InitD3D(_hInstance, WndProc, WIDTH, HEIGHT, true, D3DDEVTYPE_HAL, &pMyDevice, mainWindow))
	{
		throw std::exception("InitD3D Failed");
	}

	//Initializing keyboard
	if (DI_OK != DirectInput8Create(_hInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&dInput8, NULL))
	{
		throw std::exception("Creating of DirectInput failed");
	}
	if (DI_OK != dInput8->CreateDevice(GUID_SysKeyboard, &diKeyboard, NULL))
	{
		throw std::exception("Creating of keybaord device failed");
	}
	diKeyboard->SetDataFormat(&c_dfDIKeyboard);
	diKeyboard->SetCooperativeLevel(mainWindow, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
	diKeyboard->Acquire();

	//Initializing mouse
	if (DI_OK != dInput8->CreateDevice(GUID_SysMouse, &diMouse, NULL))
	{
		throw std::exception("Creating of mouse device failed");
	}
	diMouse->SetDataFormat(&c_dfDIMouse);
	diMouse->SetCooperativeLevel(mainWindow, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
	diMouse->Acquire();

	new FPSCounter();

	SetupView();

	m_board.Init();

	_audioPlayer->Init();

	CreateGTPThread();
	
	//Creating sprite for rendering text
	D3DXCreateSprite(pMyDevice, &m_interfaceSprite);
	D3DXCreateTextureFromFile(pMyDevice, "arrow_up.tga", &m_interfaceTexture);
	soundVolume.SetDevice(pMyDevice);
}


/////////////////////////////////////////////////////////////////////////////////


void GameEngine::CreateGTPThread()
{
	::CreateThread(NULL, 0, (unsigned long (__stdcall *)(void *))ThreadGTP,
				   (LPDWORD)this, 0, NULL);
}


/////////////////////////////////////////////////////////////////////////////////


void GameEngine::Run()
{
	MSG  msg;
    msg.message = WM_NULL;

	static float lastTime = static_cast<float>(timeGetTime());
	float currTime(0.0f);
	float timeDelta(0.0f);

	_audioPlayer->Play("01 - Tender Sugar.ogg");

	while(msg.message != WM_QUIT)
	{
		if(PeekMessage( &msg, 0, 0, 0, PM_REMOVE ))
		{
            TranslateMessage( &msg );
            DispatchMessage( &msg );
		}
		else
        {	
			timeDelta	= 0;
			currTime	= static_cast<float>(timeGetTime());
			timeDelta	= (currTime - lastTime) * 0.001f;
			Render(timeDelta);
			lastTime	= currTime;
        }
    }

	_isQuiting = true;
}


/////////////////////////////////////////////////////////////////////////////////


void GameEngine::Render(float delta)
{
	pMyDevice->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0);
	pMyDevice->BeginScene();

	if (processInput)
		ProcessInput(delta);

	//static D3DXVECTOR3 pos(0.0f, 0.0f, -5.0f);
	/*static float cameraYAngel = -D3DX_PI * 0.5f;
	static float cameraXAngel = -D3DX_PI * 0.5f;*/
	D3DXVECTOR3 pos(sinf(cameraYAngle)*cosf(cameraXAngle)*DIST_TO_BOARD, 
					cosf(cameraYAngle)*DIST_TO_BOARD, 
					-sinf(cameraYAngle)*sinf(cameraXAngle)*DIST_TO_BOARD);
	D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
	D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
	D3DXMATRIX view;
	D3DXMatrixLookAtLH(&view, &pos, &target, &up);
	pMyDevice->SetTransform(D3DTS_VIEW, &view);

	D3DXMATRIX proj;
	D3DXMatrixPerspectiveFovLH(&proj, D3DX_PI * 0.25, (float)WIDTH / (float)HEIGHT, 1.0f, 1000.0f);

	pMyDevice->SetTransform( D3DTS_PROJECTION, &proj );

	m_board.Render();

	//////////////////////////////////////////
	//Start and end sprite drawing operations
	//////////////////////////////////////////
	//pMyDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
	m_interfaceSprite->Begin(D3DXSPRITE_ALPHABLEND);
	FPSCounter::GetInstance().Display(m_interfaceSprite, delta);
	// Render volume info
	std::string text("Volume: " + util::intToString( _audioPlayer->GetVolume() ));
	TextRenderer::GetInstance().Render(m_interfaceSprite, 600, 500, text, D3DCOLOR_RGBA(2, 255, 255, 255));

	m_interfaceSprite->Flush();
	
	//D3DXMATRIX m;
	//D3DXMatrixTranslation(&m, 750.0f, 100.0f, 0.0f);
	//m_interfaceSprite->SetTransform(&m);
	//pMyDevice->SetTexture(0, m_interfaceTexture);

	
	/*
	D3DXMATRIX m;
	D3DXMatrixTranslation(&m, 750.0f, 100.0f, 0.0f);
	pMyDevice->SetTransform(D3DTS_WORLD, &m);
	pMyDevice->SetTexture(0, m_interfaceTexture);

	
	pMyDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_CONSTANT);
	pMyDevice->SetTextureStageState(0, D3DTSS_CONSTANT, 0x77000000);
	pMyDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
	//pMyDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
	//pMyDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_CONSTANT);
	//pMyDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_ADD);
	//pMyDevice->SetTextureStageState(0, D3DTSS_CONSTANT, 0xccffffff);
	//pMyDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
	//pMyDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
	pMyDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
	m_interfaceSprite->Draw(m_interfaceTexture, NULL, NULL, NULL, D3DCOLOR_ARGB(255, 255, 255, 100));
	*/
	m_interfaceSprite->End();
	
	//m_interfaceSprite->Draw(m_interfaceTexture, NULL, NULL, NULL, D3DCOLOR_ARGB(255, 255, 255, 150));

	//m_interfaceSprite->End();




	//ID3DXMesh* mesh;
	//D3DXCreateTeapot(pMyDevice,&mesh, NULL);
	//D3DXMATRIX world;
	//D3DXMatrixIdentity(&world);
	//pMyDevice->SetMaterial(&d3d::WHITE_MTRL);
	//mesh->DrawSubset(0);
	//pMyDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
	soundVolume.Render();
	pMyDevice->EndScene();
	pMyDevice->Present(0, 0, 0, 0);
}


/////////////////////////////////////////////////////////////////////////////////


DWORD GameEngine::ThreadGTP(LPWORD lpData)
{
	GameEngine* engine = (GameEngine*)(lpData);

	//::MessageBox(0, "Game finished", 0, 0);

	while(!engine->IsGameOver())
	{
		engine->_activePlayer->MakeMove();
		engine->m_board.Reload();
		engine->SwitchPlayer();
	}

	::MessageBox(0, "Game finished", 0, 0);

	return 0;
}


/////////////////////////////////////////////////////////////////////////////////


bool GameEngine::IsGameOver() 
{	
	if (_isQuiting)
		return true;

	if (_activePlayer->IsLastMovePass())
	{
		_passCount++;
		if (_passCount == 2)
			return true;
	}
	else
	{
		_passCount = 0;
	}

	return false;
}


/////////////////////////////////////////////////////////////////////////////////


void GameEngine::SwitchPlayer()
{
	_activePlayer = (_activePlayer == m_playerBlack) ? m_playerWhite : m_playerBlack;
}


/////////////////////////////////////////////////////////////////////////////////

void GameEngine::ProcessInput(float delta)
{
	//PROCESS KEYBOARD
	#define KEYDOWN(name, key) (name[key] & 0x80) 

    char     buffer[256];
	ZeroMemory(buffer, 256);
	while (diKeyboard->GetDeviceState(sizeof(buffer),(LPVOID)&buffer) == DIERR_INPUTLOST); 
    { 
		diKeyboard->Acquire();
    } 

    if (KEYDOWN(buffer, DIK_RIGHT))
	{
		cameraXAngle += 1.1f * delta;
	}
    else if(KEYDOWN(buffer, DIK_LEFT))
	{
		cameraXAngle -= 1.1f * delta;
	}


    if (KEYDOWN(buffer, DIK_UP))
	{
		cameraYAngle += 1.1f * delta;
		if (cameraYAngle > 0.0f)
			cameraYAngle = -0.01;
	}
    else if (KEYDOWN(buffer, DIK_DOWN))
	{
		cameraYAngle -= 1.1f * delta;
		if (cameraYAngle < -D3DX_PI)
			cameraYAngle = -D3DX_PI + 0.01f;
	}

	if (KEYDOWN(buffer, DIK_Q))
	{
		_audioPlayer->VolumeUp();
	}
	else if (KEYDOWN(buffer, DIK_A))
	{
		_audioPlayer->VolumeDown();
	}

	if (KEYDOWN(buffer, DIK_ESCAPE))
	{
		::DestroyWindow(mainWindow);
	}

	//////////////////////////////////////////

	//PROCESS MOUSE

	DIMOUSESTATE mouseState;
	ZeroMemory(&mouseState, sizeof(mouseState));
	while (diMouse->GetDeviceState(sizeof(mouseState),&mouseState) == DIERR_INPUTLOST); 
    { 
		diMouse->Acquire();
    }

	if (mouseState.rgbButtons[1] & 0x80)
	{
		if (mouseState.lX > 0)
		{
			cameraXAngle += 1.1f * delta * mouseState.lX;
		}
		else if(mouseState.lX < 0)
		{
			cameraXAngle -= 1.1f * delta * (-mouseState.lX);
		}


		if (mouseState.lY > 0)
		{
			cameraYAngle += 1.1f * delta * mouseState.lY;
			if (cameraYAngle > 0.0f)
				cameraYAngle = -0.01;
		}
		else if (mouseState.lY < 0)
		{
			cameraYAngle -= 1.1f * delta * (-mouseState.lY);
			if (cameraYAngle < -D3DX_PI)
				cameraYAngle = -D3DX_PI + 0.01f;
		}
	}
	//////////////////////////////////////////
}

/////////////////////////////////////////////////////////////////////////////////


/*
	Description: returns true if button event was processed!
*/

bool GameEngine::MouseButtonDown(int x, int y, MouseButtons button)
{
	switch (button)
	{
	case MouseButtons::LeftMouseButton:
		soundVolume.MouseButtonDown(x, y, button);
		break;
	}
	return true;
}

bool GameEngine::MouseButtonUp(int x, int y, MouseButtons button)
{
	switch (button)
	{
	case MouseButtons::LeftMouseButton:
		soundVolume.MouseButtonUp(x, y, button);

		break;
	}
	return true;
}